﻿using Sidvall.IdentityManager.Business;
using Sidvall.IdentityManager.Net.SimpleServices.V1;
using Sidvall.IdentityManager.Security;
using Sidvall.Security;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.IdentityManager
{
    public partial class SystemContext
    {
        #region Public Members

        public int? ActiveServerItemId { get; set; }
        public Sidvall.IdentityManager.Net.Mail.IMailContextProvider MailContextProvider { get; set; }

        #region ActiveServerItem

        private ServerItem _ActiveServerItem;
        public ServerItem ActiveServerItem
        {
            get
            {
                if (_ActiveServerItem != null)
                    return _ActiveServerItem;
                if (this.UserConfiguration == null)
                    return null;
                if (this.UserConfiguration.ServerItems == null)
                    return null;
                if (this.ActiveServerItemId == null)
                    return null;
                return (from o in this.UserConfiguration.ServerItems
                        where o.ServerItemId == this.ActiveServerItemId
                        select o).FirstOrDefault();
            }
            set
            {
                _ActiveServerItem = value;
                InitializeAuthenticationStrategy();
            }
        }

        #endregion
        #region AuthenticationStrategy

        public Sidvall.Security.AuthenticationStrategy AuthenticationStrategy
        {
            get
            {
                return Sidvall.SystemContext.Current.AuthenticationStrategyManager?.AuthenticationStrategy as Sidvall.Security.AuthenticationStrategy;
            }
        }

        #endregion
        #region DataPortalStrategyFacade

        public Sidvall.DataPortal.DataPortalStrategy DataPortalStrategyFacade
        {
            get
            {
                if (this.DataPortalStrategy as Sidvall.DataPortal.DataPortalStrategy == null)
                {
                    lock (_Mutex)
                    {
                        if (this.DataPortalStrategy as Sidvall.DataPortal.DataPortalStrategy == null)
                        {
                            var item = new Sidvall.DataPortal.DataPortalStrategy()
                            {
                                MasterAuthenticationStrategy = this.AuthenticationStrategy,
                                ServerConnectionProvider = this.ServerConnectionProvider,
                            };
                            this.DataPortalStrategy = item;
                        }
                    }
                }
                return this.DataPortalStrategy as Sidvall.DataPortal.DataPortalStrategy;
            }
        }

        #endregion
        #region PasswordHasher

        private System.Lazy<IPasswordHasher> _PasswordHasher = new System.Lazy<IPasswordHasher>(GetPasswordHasher);
        public IPasswordHasher PasswordHasher
        {
            get
            {
                return _PasswordHasher.Value;
            }
        }

        #endregion
        #region ServerConnectionProvider

        private System.Lazy<ServerConnectionProvider> _ServerConnectionProvider = new System.Lazy<ServerConnectionProvider>();
        public ServerConnectionProvider ServerConnectionProvider
        {
            get
            {
                return _ServerConnectionProvider.Value;
            }
        }

        #endregion
        #region ServerContent

        public ServerContent ServerContent
        {
            get
            {
                return Sidvall.SystemContext.Current.PrincipalContextManager.GetSessionValue<ServerContent>(ParameterManager.ServerContentKey);
            }
            set
            {
                if (value != null)
                {
                    if (value.ResourceItems == null)
                        value.ResourceItems = new Sidvall.Security.ResourceItemCollection();
                    Sidvall.SystemContext.Current.PrincipalContextManager.SetSessionValue(ParameterManager.ServerContentKey, value);
                }
                else
                {
                    Sidvall.SystemContext.Current.PrincipalContextManager.RemoveSessionValue(ParameterManager.ServerContentKey);
                }
            }
        }

        #endregion
        #region UserConfiguration

        private UserConfiguration _UserConfiguration;
        public UserConfiguration UserConfiguration
        {
            get
            {
                return _UserConfiguration;
            }
            set
            {
                _UserConfiguration = value;
                if ((_UserConfiguration != null) && (_UserConfiguration.ServerItems != null))
                {
                    var serverItem = (from o in _UserConfiguration.ServerItems
                                      where o.ServerItemId == this.ActiveServerItemId
                                      select o).FirstOrDefault();
                    if (serverItem == null)
                    {
                        serverItem = (from o in _UserConfiguration.ServerItems
                                      where o.ServerItemId == _UserConfiguration.DefaultServerItemId
                                      select o).FirstOrDefault();
                        if (serverItem != null)
                        {
                            this.ActiveServerItemId = _UserConfiguration.DefaultServerItemId;
                        }
                        else
                        {
                            if (_UserConfiguration.ServerItems.Count > 0)
                                this.ActiveServerItemId = _UserConfiguration.ServerItems.FirstOrDefault().ServerItemId;
                        }
                    }
                }
                InitializeAuthenticationStrategy();
            }
        }

        #endregion
        #region UserTokenManager

        private System.Lazy<IUserTokenManager> _UserTokenManager = new System.Lazy<IUserTokenManager>(GetUserTokenManager);
        public IUserTokenManager UserTokenManager
        {
            get
            {
                return _UserTokenManager.Value;
            }
        }

        #endregion
        #region Version

        private string _Version;
        public string Version
        {
            get
            {
                return _Version;
            }
            set
            {
                _Version = GetValidVersion(value);
            }
        }

        #endregion

        #region AddFileStorageServerConnection

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public Sidvall.DataPortal.ServerConnection AddFileStorageServerConnection(string path)
        {
            var item = GetFileStorageServerConnection(path);
            this.DataPortalStrategyFacade.ServerConnections.Add(item);
            return item;
        }

        #endregion
        #region AddSqlServerConnection

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public Sidvall.DataPortal.ServerConnection AddSqlServerConnection(string connectionString)
        {
            return AddSqlServerConnection(connectionString, null);
        }
        public Sidvall.DataPortal.ServerConnection AddSqlServerConnection(string connectionString, string key)
        {
            var item = GetSqlServerConnection(connectionString, key);
            this.DataPortalStrategyFacade.ServerConnections.Add(item);
            return item;
        }

        #endregion
        #region AddWebServiceConnection

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public Sidvall.DataPortal.ServerConnection AddWebServiceConnection(string address)
        {
            return AddWebServiceConnection(address, null);
        }
        public Sidvall.DataPortal.ServerConnection AddWebServiceConnection(string address, string key)
        {
            var item = GetWebServiceConnection(address, key);
            this.DataPortalStrategyFacade.ServerConnections.Add(item);
            return item;
        }

        #endregion
        #region GetFileStorageServerConnection

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public Sidvall.DataPortal.ServerConnection GetFileStorageServerConnection(string path)
        {
            return Sidvall.DataPortal.ServerConnectionManager.GetFileStorageServerConnection(path, null, null,
                "Sidvall.IdentityManager.Data.IO.Model.StorageManagerFactory, Sidvall.IdentityManager.Data.IO", null);
        }

        #endregion
        #region GetSqlServerConnection

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public Sidvall.DataPortal.ServerConnection GetSqlServerConnection(string connectionString, string key)
        {
            return Sidvall.DataPortal.ServerConnectionManager.GetSqlServerConnection(connectionString, "Sidvall.IdentityManager.Data.Entity", key, null,
                "Sidvall.IdentityManager.Data.Entity.Model.StorageManagerFactory, Sidvall.IdentityManager.Data.Entity", null);
        }

        #endregion
        #region GetWebServiceConnection

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public Sidvall.DataPortal.ServerConnection GetWebServiceConnection(string address, string key)
        {
            return Sidvall.DataPortal.ServerConnectionManager.GetWebServiceConnection(address, key, null, null);
        }

        #endregion

        #region InitializeAuthenticationStrategy

        public void InitializeAuthenticationStrategy()
        {
            if (this.AuthenticationStrategy == null)
                return;
            this.AuthenticationStrategy.AuthorityTokenEndpoint = Sidvall.IdentityManager.Security.SecurityManager.AuthorityTokenEndpoint;
            this.AuthenticationStrategy.ClientId = Sidvall.IdentityManager.Configuration.SettingsManager.STSClientIdResolved;
            this.AuthenticationStrategy.ClientSecret = Sidvall.IdentityManager.Configuration.SettingsManager.STSClientSecretResolved;
        }

        #endregion
        #region InitializeClientDataLayer

        public void InitializeClientDataLayer()
        {
            var codeManager = new Sidvall.Reflection.CodeManager(null);
            this.DataPortalStrategyFacade.ServerConnections.Clear();
        }

        #endregion
        #region InitializeFileStorageDataLayer

        public void InitializeFileStorageDataLayer(string path)
        {
            var codeManager = new Sidvall.Reflection.CodeManager(null);
            this.DataPortalStrategyFacade.ServerConnections.Clear();
            AddFileStorageServerConnection(path);
        }

        #endregion
        #region InitializeServerDataLayer

        public void InitializeServerDataLayer(string connectionString)
        {
            this.DataPortalStrategyFacade.ServerConnections.Clear();
            AddSqlServerConnection(connectionString);
        }

        #endregion
        #region InitializePluginsAsync

        public async Task InitializePluginsAsync()
        {
            if (this.ActiveServerItem == null)
                return;
            if (this.ActiveServerItem.Plugins == null)
                return;
            var tasks = new List<Task>();
            foreach (var item in this.ActiveServerItem.Plugins)
                tasks.Add(item.InitializeAsync());
            await Task.WhenAll(tasks);
        }

        #endregion

        #region LoadServerContentAsync

        public async Task<ServerContent> LoadServerContentAsync(bool refresh)
        {
            if ((refresh) || (this.ServerContent == null))
            {
                if (Sidvall.IdentityManager.SystemContext.Current.DataPortalStrategyFacade.RunLocal(string.Empty))
                {
                    var item = Sidvall.Security.SecurityManager.CreateServerContent("local");
                    this.ServerContent = item;
                }
                else
                {
                    var client = Sidvall.IdentityManager.SystemContext.Current.DataPortalStrategyFacade.GetHttpClient(string.Empty);
                    this.ServerContent = await Sidvall.Security.SecurityManager.GetServerContentAsync("api/V1/IdentityManagerServerContent/GetItem",
                        this.AuthenticationStrategy, client).ConfigureAwait(false);
                }
            }
            return this.ServerContent;
        }

        #endregion

        #region LoadUserConfigurationAsync

        public async Task<UserConfiguration> LoadUserConfigurationAsync()
        {
            string content;

            content = null;
            try
            {
                if (await Sidvall.SystemContext.Current.UserStorageManager.FileExistsAsync(Sidvall.IdentityManager.Configuration.SettingsManager.ConfigFilePath).ConfigureAwait(false))
                    content = await Sidvall.SystemContext.Current.UserStorageManager.ReadAllTextAsync(Sidvall.IdentityManager.Configuration.SettingsManager.ConfigFilePath).ConfigureAwait(false);
            }
            catch (System.Exception)
            {
                content = null;
            }
            return LoadUserConfiguration(content);
        }

        #endregion
        #region LoadUserConfiguration

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public UserConfiguration LoadUserConfiguration(string content)
        {
            UserConfiguration configuration;

            configuration = null;
            try
            {
                if (!string.IsNullOrWhiteSpace(content))
                    configuration = Sidvall.Serialization.SerializerManager.Current.Deserialize<UserConfiguration>(content);
            }
            catch
            {
                configuration = null;
            }
            return LoadUserConfiguration(configuration);
        }
        public UserConfiguration LoadUserConfiguration(UserConfiguration configuration)
        {
            configuration = Sidvall.IdentityManager.SystemContextConfig.Current.EntityFacade.UserConfigurationManager.GetValidConfiguration(configuration);
            this.UserConfiguration = configuration;

            return this.UserConfiguration;
        }

        #endregion
        #region SaveUserConfigurationAsync

        public async Task<UserConfiguration> SaveUserConfigurationAsync(UserConfiguration configuration)
        {
            string content;

            configuration = Sidvall.IdentityManager.SystemContextConfig.Current.EntityFacade.UserConfigurationManager.GetValidConfiguration(configuration);
            content = Sidvall.Serialization.SerializerManager.Current.SerializeObject(configuration);
            await Sidvall.SystemContext.Current.UserStorageManager.WriteAllTextAsync(Sidvall.IdentityManager.Configuration.SettingsManager.ConfigFilePath, content).ConfigureAwait(false);
            this.UserConfiguration = configuration;

            return this.UserConfiguration;
        }

        #endregion

        #region LoadVersionAsync

        public async Task<string> LoadVersionAsync()
        {
            string version;

            version = null;
            try
            {
                if (await Sidvall.SystemContext.Current.UserStorageManager.FileExistsAsync(Sidvall.IdentityManager.Configuration.SettingsManager.VersionFilePath).ConfigureAwait(false))
                    version = await Sidvall.SystemContext.Current.UserStorageManager.ReadAllTextAsync(Sidvall.IdentityManager.Configuration.SettingsManager.VersionFilePath).ConfigureAwait(false);
            }
            catch (System.Exception)
            {
                version = null;
            }
            this.Version = version;

            return this.Version;
        }

        #endregion
        #region SaveVersionAsync

        public async Task<string> SaveVersionAsync(string version)
        {
            version = GetValidVersion(version);
            await Sidvall.SystemContext.Current.UserStorageManager.WriteAllTextAsync(Sidvall.IdentityManager.Configuration.SettingsManager.VersionFilePath, version).ConfigureAwait(false);
            this.Version = version;

            return this.Version;
        }

        #endregion

        #region GetMailMessageAsync

        public async Task<Sidvall.Net.Mail.MailMessage> GetMailMessageAsync(Sidvall.IdentityManager.Net.Mail.MailType type, Sidvall.IdentityManager.Data.IAspNetUsers user,
            string token)
        {
            Sidvall.Net.Mail.MailMessage message;

            if (this.MailContextProvider != null)
                message = await this.MailContextProvider.GetMailMessageAsync(type).ConfigureAwait(false);
            else
                message = null;
            if (message == null)
            {
                switch (type)
                {
                    case Net.Mail.MailType.ConfirmationEmail:
                        message = new Sidvall.Net.Mail.MailMessage()
                        {
                            FromAddress = ParameterManager.ConfirmationEmailFromAddress,
                            Subject = ParameterManager.ConfirmationEmailSubject,
                            Body = ParameterManager.ConfirmationEmailBody,
                            IsBodyHtml = true,
                        };
                        break;
                    case Net.Mail.MailType.ResetPasswordEmail:
                        message = new Sidvall.Net.Mail.MailMessage()
                        {
                            FromAddress = ParameterManager.ResetPasswordEmailFromAddress,
                            Subject = ParameterManager.ResetPasswordEmailSubject,
                            Body = ParameterManager.ResetPasswordEmailBody,
                            IsBodyHtml = true,
                        };
                        break;
                    default:
                        return null;
                }
            }
            if (user != null)
            {
                if (token != null)
                    message.Body = message.Body.Replace("{0}", user.Id).Replace("{1}", token);
                else
                    message.Body = message.Body.Replace("{0}", user.Id);
                if (string.IsNullOrWhiteSpace(message.RecipientAddresses))
                    message.RecipientAddresses = user.Email;
            }
            if (string.IsNullOrWhiteSpace(message.ConnectionString))
                message.ConnectionString = ParameterManager.SmtpServer;
            return message;
        }

        #endregion
        #region GetUserTokenProvider

        public IUserTokenProvider GetUserTokenProvider()
        {
            return GetUserTokenProvider(null);
        }
        public IUserTokenProvider GetUserTokenProvider(string tokenProvider)
        {
            IUserTokenProvider item;

            if (string.IsNullOrWhiteSpace(tokenProvider))
                return this.UserTokenManager.UserTokenProvider;
            if (this.UserTokenManager.TwoFactorProviders.TryGetValue(tokenProvider, out item))
                return item;
            return this.UserTokenManager.UserTokenProvider;
        }

        #endregion

        #endregion
        #region Private Members

        #region GetPasswordHasher

        private static IPasswordHasher GetPasswordHasher()
        {
            var codeManager = new Sidvall.Reflection.CodeManager(null);
            return codeManager.CreateInstance("Sidvall.IdentityManager.Security.PasswordHasher, Sidvall.IdentityManager.Security") as IPasswordHasher;
        }

        #endregion
        #region GetUserTokenManager

        private static IUserTokenManager GetUserTokenManager()
        {
            var codeManager = new Sidvall.Reflection.CodeManager(null);
            return codeManager.CreateInstance("Sidvall.IdentityManager.Security.UserTokenManager, Sidvall.IdentityManager.Security") as IUserTokenManager;
        }

        #endregion
        #region GetValidVersion

        private static string GetValidVersion(string version)
        {
            if (string.IsNullOrWhiteSpace(version))
                return "";
            return version;
        }

        #endregion

        #endregion
    }
}